package cc.z8g.browser.core.tab

import android.annotation.SuppressLint
import android.app.AlertDialog
import android.graphics.Bitmap
import android.net.http.SslError
import android.os.Build
import android.os.Message
import android.view.LayoutInflater
import android.webkit.HttpAuthHandler
import android.webkit.SslErrorHandler
import android.webkit.URLUtil
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.annotation.RequiresApi
import cc.z8g.browser.R
import cc.z8g.browser.databinding.DialogAuthRequestBinding
import cc.z8g.browser.databinding.DialogSslWarningBinding
import cc.z8g.browser.dialog.BrowserDialog.resizeAndShow
import cc.z8g.browser.log.Logger
import cc.z8g.browser.utils.sp.UserSp
import cc.z8g.browser.ssl.SslState
import cc.z8g.browser.ssl.SslWarningSP
import cc.z8g.browser.utils.MiscUtils
import io.reactivex.rxjava3.subjects.PublishSubject
import kotlin.math.abs

/**
 * A [WebViewClient] that supports the tab adaptation.
 */
class TabWebViewClient(
    private val urlHandler: UrlHandler,
    private val headers: Map<String, String>,
) : WebViewClient() {

    /** Emits changes to the current URL. */
    val urlObservable: PublishSubject<String> = PublishSubject.create()

    /** Emits changes to the current SSL state. */
    val sslStateObservable: PublishSubject<SslState> = PublishSubject.create()

    /** Emits changes to the can go back state of the browser. */
    val goBackObservable: PublishSubject<Boolean> = PublishSubject.create()

    /** Emits changes to the can go forward state of the browser. */
    val goForwardObservable: PublishSubject<Boolean> = PublishSubject.create()
    /** web 资源请求列表 */
    val webResList = ArrayList<WebResourceRequest>()

    /** The current SSL state of the page. */
    var sslState: SslState = SslState.None
        private set

    private var currentUrl: String = ""
    private var isReflowRunning: Boolean = false
    private var zoomScale: Float = 0.0F
    private var urlWithSslError: String? = null

    override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
        super.onPageStarted(view, url, favicon)
        currentUrl = url
        urlObservable.onNext(url)
        if (urlWithSslError != url) {
            urlWithSslError = null
            sslState = if (URLUtil.isHttpsUrl(url)) {
                SslState.Valid
            } else {
                SslState.None
            }
        }
        sslStateObservable.onNext(sslState)
        webResList.clear()
    }

    override fun onPageFinished(view: WebView, url: String) {
        super.onPageFinished(view, url)
        urlObservable.onNext(url)
        goBackObservable.onNext(view.canGoBack())
        goForwardObservable.onNext(view.canGoForward())
    }


    override fun onScaleChanged(view: WebView, oldScale: Float, newScale: Float) {
        if (view.isShown && UserSp.getBoolean(UserSp.KEY_TEXT_REFLOW)) {
            if (isReflowRunning)
                return
            val changeInPercent = abs(100 - 100 / zoomScale * newScale)
            if (changeInPercent > 2.5f && !isReflowRunning) {
                isReflowRunning = view.postDelayed({
                    zoomScale = newScale
                    view.evaluateJavascript(MiscUtils.TextReflow) { isReflowRunning = false }
                }, 100)
            }

        }
    }

    override fun onReceivedHttpAuthRequest(
        view: WebView,
        handler: HttpAuthHandler,
        host: String,
        realm: String
    ) {
        val context = view.context
        AlertDialog.Builder(context).apply {
            val dialogView = DialogAuthRequestBinding.inflate(LayoutInflater.from(context))

            val realmLabel = dialogView.authRequestRealmTextview
            val name = dialogView.authRequestUsernameEdittext
            val password = dialogView.authRequestPasswordEdittext

            realmLabel.text = context.getString(R.string.label_realm, realm)

            setView(dialogView.root)
            setTitle(R.string.title_sign_in)
            setCancelable(true)
            setPositiveButton(R.string.title_sign_in) { _, _ ->
                val user = name.text.toString()
                val pass = password.text.toString()
                handler.proceed(user.trim(), pass.trim())
                Logger.d("TabWebViewClient", "Attempting HTTP Authentication")
            }
            setNegativeButton(R.string.action_cancel) { _, _ ->
                handler.cancel()
            }
        }.resizeAndShow()
    }

    override fun onFormResubmission(view: WebView, dontResend: Message, resend: Message) {
        val context = view.context
        AlertDialog.Builder(context).apply {
            setTitle(context.getString(R.string.title_form_resubmission))
            setMessage(context.getString(R.string.message_form_resubmission))
            setCancelable(true)
            setPositiveButton(context.getString(R.string.action_yes)) { _, _ ->
                resend.sendToTarget()
            }
            setNegativeButton(context.getString(R.string.action_no)) { _, _ ->
                dontResend.sendToTarget()
            }
        }.resizeAndShow()
    }

    @SuppressLint("WebViewClientOnReceivedSslError")
    override fun onReceivedSslError(webView: WebView, handler: SslErrorHandler, error: SslError) {
        val context = webView.context
        urlWithSslError = webView.url

        sslState = SslState.Invalid(error)
        sslStateObservable.onNext(sslState)
        sslState = SslState.Invalid(error)

        when (SslWarningSP.recallBehaviorForDomain(webView.url)) {
            SslWarningSP.Behavior.PROCEED -> return handler.proceed()
            SslWarningSP.Behavior.CANCEL -> return handler.cancel()
            null -> Unit
        }

        val errorCodeMessageCodes = error.getAllSslErrorMessageCodes()

        val stringBuilder = StringBuilder()
        for (messageCode in errorCodeMessageCodes) {
            stringBuilder.append(" - ").append(context.getString(messageCode)).append('\n')
        }
        val alertMessage = context.getString(R.string.message_insecure_connection, stringBuilder.toString())

        AlertDialog.Builder(context).apply {
            val view = DialogSslWarningBinding.inflate(LayoutInflater.from(context))
            val notAskAgain = view.checkBoxDontAskAgain
            setTitle(context.getString(R.string.title_warning))
            setMessage(alertMessage)
            setCancelable(true)
            setView(view.root)
            setOnCancelListener { handler.cancel() }
            setPositiveButton(context.getString(R.string.action_yes)) { _, _ ->
                if (notAskAgain.isChecked) {
                    SslWarningSP.rememberBehaviorForDomain(webView.url.orEmpty(), SslWarningSP.Behavior.PROCEED)
                }
                handler.proceed()
            }
            setNegativeButton(context.getString(R.string.action_no)) { _, _ ->
                if (notAskAgain.isChecked) {
                    SslWarningSP.rememberBehaviorForDomain(webView.url.orEmpty(), SslWarningSP.Behavior.CANCEL)
                }
                handler.cancel()
            }
        }.resizeAndShow()
    }

    @Deprecated("Deprecated in Java")
    override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
        return urlHandler.shouldOverrideLoading(view, url, headers) || super.shouldOverrideUrlLoading(view, url)
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
        return urlHandler.shouldOverrideLoading(view, request.url.toString(), headers) || super.shouldOverrideUrlLoading(view, request)
    }

    /**
     * ```
     *  return WebResourceResponse("text/plain", "utf-8", ByteArrayInputStream(byteArrayOf())) // 广告拦截
     * ```
     */
    override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
        Logger.d("TabWebViewClient", "#shouldInterceptRequest " + request.method
                + " isForMainFrame=" + request.isForMainFrame
                + " " + request.url + " " + request.requestHeaders)
        webResList.add(request)
        return null
    }

    private fun SslError.getAllSslErrorMessageCodes(): List<Int> {
        val errorCodeMessageCodes = ArrayList<Int>(1)

        if (hasError(SslError.SSL_DATE_INVALID)) {
            errorCodeMessageCodes.add(R.string.message_certificate_date_invalid)
        }
        if (hasError(SslError.SSL_EXPIRED)) {
            errorCodeMessageCodes.add(R.string.message_certificate_expired)
        }
        if (hasError(SslError.SSL_IDMISMATCH)) {
            errorCodeMessageCodes.add(R.string.message_certificate_domain_mismatch)
        }
        if (hasError(SslError.SSL_NOTYETVALID)) {
            errorCodeMessageCodes.add(R.string.message_certificate_not_yet_valid)
        }
        if (hasError(SslError.SSL_UNTRUSTED)) {
            errorCodeMessageCodes.add(R.string.message_certificate_untrusted)
        }
        if (hasError(SslError.SSL_INVALID)) {
            errorCodeMessageCodes.add(R.string.message_certificate_invalid)
        }

        return errorCodeMessageCodes
    }

}
